package ro.gillardon.business.priceservice.impl.train;

import static ro.gillardon.commons.config.ConfigParam.BUSINESS_PRICESERVICE_TRAIN_NAVIGATOR_DATEFORMAT;
import static ro.gillardon.commons.config.ConfigParam.BUSINESS_PRICESERVICE_TRAIN_NAVIGATOR_DEPARTUREDATE_NAME;
import static ro.gillardon.commons.config.ConfigParam.BUSINESS_PRICESERVICE_TRAIN_NAVIGATOR_FIRSTCLASS_ID;
import static ro.gillardon.commons.config.ConfigParam.BUSINESS_PRICESERVICE_TRAIN_NAVIGATOR_FROM_ID;
import static ro.gillardon.commons.config.ConfigParam.BUSINESS_PRICESERVICE_TRAIN_NAVIGATOR_RESULT_XPATH;
import static ro.gillardon.commons.config.ConfigParam.BUSINESS_PRICESERVICE_TRAIN_NAVIGATOR_SEARCH_CLASS;
import static ro.gillardon.commons.config.ConfigParam.BUSINESS_PRICESERVICE_TRAIN_NAVIGATOR_SECONDCLASS_ID;
import static ro.gillardon.commons.config.ConfigParam.BUSINESS_PRICESERVICE_TRAIN_NAVIGATOR_TO_ID;
import static ro.gillardon.commons.config.ConfigParam.BUSINESS_PRICESERVICE_TRAIN_NAVIGATOR_URL;
import static ro.gillardon.commons.config.ConfigParam.HTTP_PROXY_HOST;
import static ro.gillardon.commons.config.ConfigParam.HTTP_PROXY_PORT;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

import org.openqa.selenium.By;
import org.openqa.selenium.Proxy;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.remote.CapabilityType;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

import ro.gillardon.commons.config.ConfigParam;
import ro.gillardon.commons.config.ConfigProvider;
import ro.gillardon.commons.enums.City;
import ro.gillardon.commons.mot.MotClass;
import ro.gillardon.commons.mot.train.TrainClass;
import ro.gillardon.commons.mot.train.TrainConditions;

/**
 * Contains methods to navigate through the bahn.de web page, fill input information and retrieve results. Usage can be
 * found in corresponding test class.
 *
 * @author davidl
 */
public class DBahnNavigator implements AutoCloseable {
  /** The Constant PROXY. */
  private static final String PROXY;

  /** The Constant URL. */
  private static final String URL;

  /** The Constant FROM_ID. */
  private static final String FROM_ID;

  /** The Constant TO_ID. */
  private static final String TO_ID;

  /** The Constant SEARCH_BUTTON_CLASS. */
  private static final String SEARCH_BUTTON_CLASS;

  /** The Constant DEPARTURE_DATE_NAME. */
  private static final String DEPARTURE_DATE_NAME;

  /** The Constant DATE_FORMAT_PATTERN. */
  private static final String DATE_FORMAT_PATTERN;

  /** The Constant CLASS_2_ID. */
  private static final String CLASS_2_ID;

  /** The Constant CLASS_1_ID. */
  private static final String CLASS_1_ID;

  /** The Constant RESULT_XPATH. */
  private static final String RESULT_XPATH;

  // initialize the fields
  static {
    // @formatter:off
    PROXY               = getProperty(HTTP_PROXY_HOST) + ":" + getProperty(HTTP_PROXY_PORT);
    URL                 = getProperty(BUSINESS_PRICESERVICE_TRAIN_NAVIGATOR_URL);
    FROM_ID             = getProperty(BUSINESS_PRICESERVICE_TRAIN_NAVIGATOR_FROM_ID);
    TO_ID               = getProperty(BUSINESS_PRICESERVICE_TRAIN_NAVIGATOR_TO_ID);
    SEARCH_BUTTON_CLASS = getProperty(BUSINESS_PRICESERVICE_TRAIN_NAVIGATOR_SEARCH_CLASS);
    DEPARTURE_DATE_NAME = getProperty(BUSINESS_PRICESERVICE_TRAIN_NAVIGATOR_DEPARTUREDATE_NAME);
    DATE_FORMAT_PATTERN = getProperty(BUSINESS_PRICESERVICE_TRAIN_NAVIGATOR_DATEFORMAT);
    CLASS_2_ID          = getProperty(BUSINESS_PRICESERVICE_TRAIN_NAVIGATOR_SECONDCLASS_ID);
    CLASS_1_ID          = getProperty(BUSINESS_PRICESERVICE_TRAIN_NAVIGATOR_FIRSTCLASS_ID);
    RESULT_XPATH        = getProperty(BUSINESS_PRICESERVICE_TRAIN_NAVIGATOR_RESULT_XPATH);
    // @formatter:on
  }

  /**
   * Helper method to initialize fields.
   *
   * @param param the configParam
   * @return the property value
   */
  private static String getProperty(final ConfigParam param) {
    return ConfigProvider.getProperty(param);
  }

  /** The driver. */
  private final WebDriver driver;

  /**
   * Instantiates a new train navigator.
   */
  public DBahnNavigator() {
    Proxy proxy = new Proxy().setHttpProxy(PROXY).setFtpProxy(PROXY).setSslProxy(PROXY);
    DesiredCapabilities cap = new DesiredCapabilities();
    cap.setCapability(CapabilityType.PROXY, proxy);
    cap.setCapability(CapabilityType.SUPPORTS_JAVASCRIPT, true);
    this.driver = new FirefoxDriver(cap);
  }

  /**
   * Closes the driver.
   */
  @Override
  public void close() {
    this.driver.close();
  }

  /**
   * Navigate to the DBahn page.
   */
  public void navigateToPage() {
    this.driver.navigate().to(URL);
  }

  /**
   * Fill required info (from, to, date, class).
   *
   * @param conditions the wrapper class which contains the above mentioned parameters
   */
  public void fillRequiredInfo(final TrainConditions conditions) {
    setFrom(conditions.getFrom());
    setTo(conditions.getTo());
    setDepartureDate(conditions.getDepartureDate());
    setClass(conditions.getMotClass());

  }

  /**
   * Sets the train class.
   *
   * @param motClass the new class
   */
  private void setClass(final MotClass motClass) {
    String classIdToClick = TrainClass.SECOND_CLASS == motClass ? CLASS_2_ID : CLASS_1_ID;
    WebElement classRadioButton = this.driver.findElement(By.id(classIdToClick));
    classRadioButton.click();
  }

  /**
   * Sets the departure date.
   *
   * @param departureDate the new departure date
   */
  private void setDepartureDate(final LocalDate departureDate) {
    WebElement toElement = this.driver.findElement(By.name(DEPARTURE_DATE_NAME));
    toElement.clear();
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT_PATTERN);
    String formattedDepartureDate = departureDate.format(formatter);
    toElement.sendKeys(formattedDepartureDate);
  }

  /**
   * Click the search button.
   */
  public void search() {
    WebElement search = this.driver.findElement(By.className(SEARCH_BUTTON_CLASS));
    search.click();
  }

  /**
   * Fills the destination information.
   *
   * @param city the destination city enum
   */
  private void setTo(final City city) {
    WebElement toElement = this.driver.findElement(By.id(TO_ID));
    toElement.clear();
    toElement.sendKeys(city.getName());
  }

  /**
   * Fills the starting point information.
   *
   * @param city the starting point city enum
   */
  private void setFrom(final City city) {
    WebElement fromElement = this.driver.findElement(By.id(FROM_ID));
    fromElement.clear();
    fromElement.sendKeys(city.getName());
  }

  /**
   * Gets the result table from the result page.
   *
   * @return the raw HTML table result
   */
  public String getResults() {
    WebDriverWait wait = new WebDriverWait(this.driver, 10);
    WebElement result = wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath(RESULT_XPATH)));
    return "<table>" + result.getAttribute("innerHTML") + "</table>";
  }
}
